package generator

import (
	"fmt"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"v8s/compose/model"
)

type (
	ServiceGenerator struct{}
)

const (
	podSelectorLabel = "platform-v.drop-app/service"
)

var (
	serviceMeta = metav1.TypeMeta{
		Kind:       "Service",
		APIVersion: "v1",
	}
)

func NewServiceGenerator() *ServiceGenerator {
	return &ServiceGenerator{}
}

func (g *ServiceGenerator) Generate(services model.Services) ([]*corev1.Service, error) {
	results := make([]*corev1.Service, 0, len(services)+1)

	for name, service := range services {
		generated, err := g.generate(name, service)
		if err != nil {
			return nil, fmt.Errorf("failed to generate services for service '%s': %w", name, err)
		}

		for _, generatedService := range generated {
			current := generatedService

			results = append(results, current)
		}
	}

	return results, nil
}

func (g *ServiceGenerator) GetSelector(name model.ServiceName) PodSelector {
	return PodSelector{podSelectorLabel: string(name)}
}

func (g *ServiceGenerator) generate(name model.ServiceName, service model.Service) ([]*corev1.Service, error) {
	if len(service.Ports) == 0 {
		return nil, nil
	}

	uniquePorts := map[string]*model.PortConfiguration{}

	for _, port := range service.Ports {
		configuration, err := port.ToPortConfiguration()
		if err != nil {
			return nil, fmt.Errorf("failed to get port configuration for service '%s': %w", name, err)
		}

		uniquePorts[fmt.Sprintf("%s-%d", configuration.Protocol, configuration.ContainerPort)] = configuration
	}

	ports := sliceOrNil[corev1.ServicePort](len(uniquePorts))

	for portName, port := range uniquePorts {
		ports = append(ports, corev1.ServicePort{
			Name:       portName,
			Port:       port.ContainerPort,
			TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: port.ContainerPort},
		})
	}

	return []*corev1.Service{{
		TypeMeta: serviceMeta,
		ObjectMeta: metav1.ObjectMeta{
			Name: string(name),
			Annotations: map[string]string{
				ResourceBelongsToAnnotation: string(name),
			},
		},
		Spec: corev1.ServiceSpec{
			Ports:    ports,
			Selector: g.GetSelector(name),
		},
	}}, nil
}
